package com.yeyks.other.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.yeyks.common.appcommon.dto.AppAddressCode;
import com.yeyks.common.appcommon.dto.AppAddressDTO;
import com.yeyks.common.appcommon.dto.account.AppAccountDTO;
import com.yeyks.common.appcommon.param.QueryAddressParam;
import com.yeyks.common.appcommon.result.CommonResult;
import com.yeyks.common.appcommon.result.ThreadLocalUtil;
import com.yeyks.common.utils.ObjectConvertUtil;
import com.yeyks.common.utils.apputil.bean.MyBeanUtils;
import com.yeyks.common.utils.apputil.redis.RedisService;
import com.yeyks.common.utils.apputil.rediskey.RedisKey;
import com.yeyks.other.dal.dao.AddressCodeMapper;
import com.yeyks.other.dal.dao.AddressDivisionMapper;
import com.yeyks.other.dal.domain.AddressCode;
import com.yeyks.other.dal.domain.AddressDivision;
import com.yeyks.other.model.AddressDivisionDTO;
import com.yeyks.other.service.AddressCodeService;
import com.yeyks.other.service.AddressDivisionService;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * <p>
 * 地址信息表 服务实现类
 * </p>
 *
 * @author yun
 * @since 2019-06-25
 */
@Service
@Slf4j
public class AddressDivisionServiceImpl extends ServiceImpl<AddressDivisionMapper, AddressDivision> implements AddressDivisionService {


    private static final String INDEX_URL = "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2018/";
    private static final String CHARSET_NAME_GB2312 = "GB2312";
    private static final String CHARSET_NAME_GBK = "GBK";
    private static final String[] CLASS_NAMES = {".citytr", ".countytr"};

    /**
     * 数据查找规则，针对fullName的修改
     */
    private static final String[] PROVINCES = {"重庆", "上海", "北京", "天津"};

    private AddressDivisionMapper addressDivisionMapper;

    private AddressCodeService addressCodeService;

    @Autowired
    private RedisService redisService;

    @Autowired
    public AddressDivisionServiceImpl(AddressDivisionMapper addressDivisionMapper,
                                      AddressCodeService addressCodeService) {
        this.addressDivisionMapper = addressDivisionMapper;
        this.addressCodeService = addressCodeService;
    }

    @Override
    public List<AddressDivisionDTO> getAddressByParentCode(String divCode, String sourceType) {
        List<AddressDivisionDTO> addressDivisionDTOList = Lists.newArrayList();
        if("yyks".equals(sourceType)){
            Map<String, Object> map = Maps.newHashMap();
            if (divCode == null || StringUtils.isEmpty(divCode)) {
                map.put("div_level", 1);
            } else {
                map.put("div_code_parent", divCode);
            }
            List<AddressDivision> addressDivisionList = addressDivisionMapper.selectByMap(map);

            if (!CollectionUtils.isEmpty(addressDivisionList)) {
                addressDivisionDTOList = ObjectConvertUtil.convertList(addressDivisionList, AddressDivisionDTO.class);
            }
        }else{
            List<AddressCode> list = null;
            if(StringUtils.isEmpty(divCode)){
                list =addressCodeService.list(new QueryWrapper<AddressCode>()
                        .isNull(AddressCode.PARENT_ID));
                for(AddressCode addressCode : list){
                    AddressDivisionDTO divisionDTO = new AddressDivisionDTO();
                    divisionDTO.setDivId(String.valueOf(addressCode.getId()));
                    divisionDTO.setDivCode(addressCode.getProvinceId());
                    divisionDTO.setDivName(addressCode.getProvinceName());
                    divisionDTO.setDivLevel("1");
                    divisionDTO.setDivFullName(addressCode.getProvinceName());
                    addressDivisionDTOList.add(divisionDTO);
                }
            }else{
                list = addressCodeService.list(new QueryWrapper<AddressCode>()
                        .eq(AddressCode.PARENT_ID, divCode));
                for(AddressCode addressCode : list){
                    AddressDivisionDTO divisionDTO = new AddressDivisionDTO();
                    divisionDTO.setDivId(String.valueOf(addressCode.getId()));
                    divisionDTO.setDivCode(addressCode.getCityId());
                    divisionDTO.setDivName(addressCode.getCityName());
                    divisionDTO.setDivLevel("2");
                    divisionDTO.setDivFullName(addressCode.getProvinceName() + addressCode.getCityName());
                    divisionDTO.setDivCodeParent(addressCode.getProvinceId());
                    addressDivisionDTOList.add(divisionDTO);
                }
            }
        }
        return addressDivisionDTOList;
    }


    @Override
    public CommonResult<List<AppAddressDTO>> getAddressByCode(QueryAddressParam addressParam) {

        AppAccountDTO appAccountDTO = ThreadLocalUtil.getAccount();
        log.info("你的账户信息为[{}]", appAccountDTO);

        List<AppAddressDTO> appAddressDTOList = Lists.newArrayList();
        if (null != addressParam) {
            List<AddressDivision> addressDivisionList = addressDivisionMapper.selectChildByCode(addressParam);
            if (!CollectionUtils.isEmpty(addressDivisionList)) {
                appAddressDTOList = MyBeanUtils.convertArrayList(addressDivisionList, AppAddressDTO.class);
            }
        }
        return CommonResult.success(appAddressDTOList);
    }

    /**
     * 根据地址名称得到 code
     *
     * @param address 地址area， 账号的地址
     * @return 返回结果
     */
    @Override
    public CommonResult<String> getAddressCodeByDetailAddress(String address) {
        if (!StringUtils.isEmpty(address)) {

            List<AddressDivision> addressDivisionList = addressDivisionMapper.selectList(new QueryWrapper<AddressDivision>()
                    .eq(AddressDivision.DIV_FULL_NAME, getAddressSample(address)).eq(AddressDivision.DIV_NAME,
                            address.split("\\.")[address.split("\\.").length-1]));
            if(!CollectionUtils.isEmpty(addressDivisionList)){
                if (null != addressDivisionList.get(0)) {
                    return CommonResult.success(addressDivisionList.get(0).getDivCode());
                }
            }

        }
        return CommonResult.failed("数据未找到，请尽快更新地址库信息， 地址输入参数为：【" + address + "】");
    }


    @Override
    public CommonResult updateOrAddAllAddress() throws IOException {

        List<Map> privinceList = redisService.getHashList(RedisKey.UPDATE_ADD_ADDRESS, "address");

        if (CollectionUtils.isEmpty(privinceList)) {
            privinceList = Lists.newLinkedList();
            InputStream inputStream = new URL(INDEX_URL).openStream();
            Document doc = Jsoup.parse(inputStream, CHARSET_NAME_GB2312, INDEX_URL);
            inputStream.close();
            Elements provinceElements = doc.select(".provincetr");

            Map privinceMap;
            for (Element provinceElement : provinceElements) {
                Elements privinceLinks = provinceElement.select("a");
                for (Element privinceLink : privinceLinks) {
                    privinceMap = new LinkedHashMap();
                    privinceMap.put("name", privinceLink.text());

                    List<Map> childList;
                    while (true) {
                        // 递归获取 Child
                        getChild(privinceMap, INDEX_URL + privinceLink.attr("href"), 0);
                        childList = (List<Map>) privinceMap.get("child");
                        // 莫名其妙，不知道为什么会出现 childList 为空的情况。
                        if (!CollectionUtils.isEmpty(childList)) {
                            break;
                        }
                        System.out.println("childList 为空");
                    }

                    String code = childList.get(0).get("code").toString();
                    privinceMap.put("code", code.substring(0, 2) + "0000000000");
                    privinceList.add(privinceMap);
                }
            }
            //存入数据
            redisService.saveHashList(privinceList, RedisKey.UPDATE_ADD_ADDRESS, "address", 30, TimeUnit.DAYS);
        }
        //循环录入数据
        updateOrSaveAddress(privinceList, null, 0);
        return CommonResult.create(null, 200, "更新成功");
    }

    @Override
    public CommonResult updateAllOtherCountry() {

        //查询出所有的市
        List<AddressDivision> addressDivisionList = addressDivisionMapper.selectList(new QueryWrapper<AddressDivision>()
                .eq(AddressDivision.DIV_LEVEL, 2).eq(AddressDivision.ENABLE, 1));
        if (!CollectionUtils.isEmpty(addressDivisionList)) {
            addressDivisionList.forEach(addressDivision -> {

                String divId = addressDivision.getDivId().substring(0, 4) + "99";
                AddressDivision division = addressDivisionMapper.selectById(divId);
                if (null == division) {
                    division = new AddressDivision();
                    division.setDivId(divId);
                    division.setDivCode(divId);
                    division.setDivCodeParent(addressDivision.getDivCode());
                    division.setDivLevel("3");
                    division.setDivName("其它区");
                    division.setDivFullName(addressDivision.getDivFullName() + "其它区");
                    division.setEnable(1);
                    addressDivisionMapper.insert(division);
                }
            });
        }
        return CommonResult.success("更新成功");
    }

    @Override
    public CommonResult<AppAddressDTO> getAddressByDivId(String divCode) {

        AddressDivision addressDivision = addressDivisionMapper.selectById(divCode);
        if (null != addressDivision) {
            AppAddressDTO addressDTO = MyBeanUtils.convert(addressDivision, AppAddressDTO.class);
            return CommonResult.success(addressDTO);
        }
        return CommonResult.failed("没有这条数据");
    }

    @Override
    public CommonResult<String> getAreaByDivId(String divCode) {

        AddressDivision addressDivision = addressDivisionMapper.selectById(divCode);
        if (null != addressDivision) {
            StringBuilder areaStr = new StringBuilder();
            switch (addressDivision.getDivLevel()) {
                case "1":
                    areaStr.append(addressDivision.getDivName());
                    break;
                case "2":
                    AddressDivision parentAddress = addressDivisionMapper.selectById(addressDivision.getDivCodeParent());
                    areaStr.append(parentAddress.getDivName())
                            .append(".")
                            .append(addressDivision.getDivName());
                    break;
                case "3":
                    AddressDivision secondAddress = addressDivisionMapper.selectById(addressDivision.getDivCodeParent());
                    if (null != secondAddress) {
                        AddressDivision firstAddress = addressDivisionMapper.selectById(secondAddress.getDivCodeParent());
                        areaStr.append(firstAddress.getDivName())
                                .append(".")
                                .append(secondAddress.getDivName())
                                .append(".")
                                .append(addressDivision.getDivName());
                    }
                    break;
                default:
                    break;
            }
            return CommonResult.success(areaStr.toString());
        }
        return CommonResult.failed("主键code有误");
    }

    @Override
    public CommonResult<AppAddressCode> getAddressCodeByDivId(String divCode) {

        AppAddressCode appAddressCode = getEntityCode(null, divCode);
        if (null != appAddressCode) {
            return CommonResult.success(appAddressCode);
        }
        return CommonResult.failed("没有该数据,查询失败");
    }

    /**
     * 将 area 中的 小数点进行 去除
     *
     * @param address area地址
     * @return 返回fullAddress --》 方便直接从数据库中查询
     */
    private String getAddressSample(String address) {
        List<String> provincesList = Arrays.asList(PROVINCES);
        String[] addresses = address.split("\\.");
        StringBuilder searchAddress = new StringBuilder();
        for (String s : addresses) {
            if (provincesList.contains(s) && addresses.length > 1) {
                continue;
            }
            if ("县".equals(s)) {
                //目前只有重庆市有县级的市。
                searchAddress.append("重庆市");
                continue;
            }
            searchAddress.append(s);
        }
        return searchAddress.toString();
    }

    /**
     * 根据area地址 或者 divCode 得到 省 市 区/县的code值
     * 规则：
     * 1. 当 address 为null时, 根据divCode进行查询
     * 2. 当 divCode为 null时, 根据address 的area的全称进行查询;
     *
     * @param address area地址
     * @param divCode 主键code
     * @return 返回对象信息
     */
    @Override
    public AppAddressCode getEntityCode(String address, String divCode) {
        AppAddressCode appAddressCode = new AppAddressCode();
        AddressDivision addressDivision = null;
        if (null == divCode || "".equals(divCode)) {
            //因为可能查询到重复的值，这时候需要在集合中筛选
            List<AddressDivision> addressDivisionList = addressDivisionMapper.selectList(new QueryWrapper<AddressDivision>()
                    .eq(AddressDivision.DIV_FULL_NAME, getAddressSample(address)).eq(AddressDivision.DIV_NAME,
                            address.split("\\.")[address.split("\\.").length - 1]));
            if(!CollectionUtils.isEmpty(addressDivisionList)){
                addressDivision = addressDivisionList.get(0);
            }
        } else {
            addressDivision = addressDivisionMapper.selectById(divCode);
        }
        if (null != addressDivision) {
            switch (addressDivision.getDivLevel()) {
                //省
                case "1":
                    appAddressCode.setProvinceCode(addressDivision.getDivCode());
                    break;
                //市
                case "2":
                    appAddressCode.setProvinceCode(addressDivision.getDivCodeParent());
                    appAddressCode.setCityCode(addressDivision.getDivCode());
                    break;
                //区/县
                case "3":
                    appAddressCode.setCityCode(addressDivision.getDivCodeParent());
                    appAddressCode.setCountyCode(addressDivision.getDivCode());
                    AddressDivision addressDivisionManage = getDivCode(addressDivision.getDivCodeParent());
                    if (null != addressDivisionManage) {
                        appAddressCode.setProvinceCode(addressDivisionManage.getDivCodeParent());
                    }
                    break;
                default:
                    break;
            }
        }
        return appAddressCode;
    }

    private AddressDivision getDivCode(String code) {
        AddressDivision addressDivision = addressDivisionMapper.selectById(code);
        if (null != addressDivision) {
            return addressDivision;
        }
        return null;
    }


    private void updateOrSaveAddress(List<Map> list, String parentCode, int level) {

        if (level == CLASS_NAMES.length + 1) {
            return;
        }
        level += 1;
        if (list != null) {
            for (Map map : list) {
                AddressDivision addressDivision = new AddressDivision();
                addressDivision.setDivCodeParent(parentCode);
                addressDivision.setDivLevel(String.valueOf(level));
                String divCode = (String) map.get("code");
                String name = (String) map.get("name");
                if (!StringUtils.isEmpty(divCode)) {
                    addressDivision.setDivCode(divCode.substring(0, 6));
                    addressDivision.setDivId(divCode.substring(0, 6));
                    name = getReallyName(divCode.substring(0, 6), name);
                }
                //给完整地址设值
                if (!StringUtils.isEmpty(parentCode)) {
                    //查询父类的全称
                    AddressDivision division = addressDivisionMapper.selectById(parentCode);
                    //得到全称
                    addressDivision.setDivFullName(getFullName(division, name));
                } else {
                    addressDivision.setDivFullName(name);
                }
                if (!StringUtils.isEmpty(name)) {
                    addressDivision.setDivName(name);
                }
                addressDivision.setEnable(1);

                addressDivisionMapper.insert(addressDivision);

                updateOrSaveAddress((List<Map>) map.get("child"), addressDivision.getDivCode(), level);
            }
        }


    }

    /**
     * 北京，天津，重庆，上海的字段特殊处理
     */
    private final String[] T_CITY_CODE = {"110000", "110100", "120000", "120100", "500000", "500100", "310000", "310100"};

    private String getReallyName(String divCode, String name) {
        switch (divCode) {
            case "110000":
                name = "北京";
                break;
            case "110100":
                name = "北京市";
                break;
            case "120000":
                name = "天津";
                break;
            case "120100":
                name = "天津市";
                break;
            case "500000":
                name = "重庆";
                break;
            case "500100":
                name = "重庆市";
                break;
            case "310000":
                name = "上海";
                break;
            case "310100":
                name = "上海市";
                break;
            default:
                break;
        }
        return name;
    }

    /**
     * 查询的地址详情获取
     *
     * @param addressDivision
     * @return
     */
    private String getFullName(AddressDivision addressDivision, String name) {
        //拼接上当前的完整地址
        String fullName = addressDivision.getDivFullName() + name;
        switch (addressDivision.getDivCode()) {
            case "110000":
                fullName = "北京市";
                break;
            case "120000":
                fullName = "天津市";
                break;
            case "500000":
                fullName = "重庆市";
                break;
            case "310000":
                fullName = "上海市";
                break;
            default:
                break;
        }
        return fullName;
    }


    private void getChild(Map map, String url, int level) {
        if (level == CLASS_NAMES.length) {
            return;
        }
        System.out.println(url);

        Document doc;
        while (true) {
            try {
                OkHttpClient okHttpClient = new OkHttpClient();
                Request request = new Request.Builder().url(url).build();
                Response response = okHttpClient.newCall(request).execute();
                if (!response.isSuccessful()) {
                    continue;
                }
                byte[] bodyBytes = response.body().bytes();
                String bodyText = new String(bodyBytes, CHARSET_NAME_GB2312);
                if (bodyText.contains("?")) {
                    bodyText = new String(bodyBytes, CHARSET_NAME_GBK);
                }
                doc = Jsoup.parse(bodyText);
                break;
            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
        }

        List<Map> childList = new LinkedList<>();
        Elements elements = doc.select(CLASS_NAMES[level]);
        level += 1;
        Map childMap;
        for (Element element : elements) {
            Elements links = element.select("td a");
            // 市辖区
            boolean isContinue = true;
            if (links.size() == 0) {
                links = element.select("td");
                isContinue = false;
            }

            Element codeLink = links.first();
            childMap = new LinkedHashMap();
            childMap.put("code", codeLink.text());
            childMap.put("name", links.last().text());

            if (isContinue) {
                getChild(childMap, url.substring(0, url.lastIndexOf("/") + 1) + codeLink.attr("href"), level);
            }

            childList.add(childMap);
        }
        map.put("level", level - 1);
        map.put("child", childList);
    }
}
